05. Simple Mover: The Code

Simple Mover: The Code

Below is the complete code for the simple_mover C++ node, with line-by-line comments embedded. You can copy and paste this code into the simple_mover script you created in /home/workspace/catkin_ws/src/simple_arm/src/ directory like this:

First, open a new terminal. Then:

$ cd /home/workspace/catkin_ws/src/simple_arm/src/
$ gedit simple_mover.cpp

You have opened the C++ simple_mover script with the gedit editor, now copy and paste the code below into the script and save the script. I encourage you to write this code instead of copying it so that you get more familiar with the syntax.

simple_mover.cpp

#include "ros/ros.h"
#include "std_msgs/Float64.h"

int main(int argc, char** argv)
{
    // Initialize the arm_mover node
    ros::init(argc, argv, "arm_mover");

    // Create a handle to the arm_mover node
    ros::NodeHandle n;

    // Create a publisher that can publish a std_msgs::Float64 message on the /simple_arm/joint_1_position_controller/command topic
    ros::Publisher joint1_pub = n.advertise<std_msgs::Float64>("/simple_arm/joint_1_position_controller/command", 10);
    // Create a publisher that can publish a std_msgs::Float64 message on the /simple_arm/joint_2_position_controller/command topic
    ros::Publisher joint2_pub = n.advertise<std_msgs::Float64>("/simple_arm/joint_2_position_controller/command", 10);

    // Set loop frequency of 10Hz
    ros::Rate loop_rate(10);

    int start_time, elapsed;

    // Get ROS start time
    while (not start_time) {
        start_time = ros::Time::now().toSec();
    }

    while (ros::ok()) {
        // Get ROS elapsed time
        elapsed = ros::Time::now().toSec() - start_time;

        // Set the arm joint angles
        std_msgs::Float64 joint1_angle, joint2_angle;
        joint1_angle.data = sin(2 * M_PI * 0.1 * elapsed) * (M_PI / 2);
        joint2_angle.data = sin(2 * M_PI * 0.1 * elapsed) * (M_PI / 2);

        // Publish the arm joint angles
        joint1_pub.publish(joint1_angle);
        joint2_pub.publish(joint2_angle);

        // Sleep for the time remaining until 10 Hz is reached
        loop_rate.sleep();
    }

    return 0;
}

Simple Mover: The Code

The code: Explained

#include "ros/ros.h"

ros is the official client library for ROS. It provides most of the fundamental functionality required for interfacing with ROS via C++. It has tools for creating Nodes and interfacing with Topics, Services, and Parameters.

#include "std_msgs/Float64.h"

From the std_msgs package, the Float64 header file is imported. The std_msgs package also contains the primitive message types in ROS. Later, you will be publish Float64 messages to the position command topics for each joint.

ros::init(argc, argv, "arm_mover");

A ROS node is initialized with the init() function and registered with the ROS Master. Here arm_mover is the name of the node. Notice that the main function takes both argc and argv arguments and passes them to the init() function.

 ros::NodeHandle n;

A node handle object n is instantiated from the NodeHandle class. This node handle object will fully initialize the node and permits it to communicate with the ROS Master.

ros::Publisher joint1_pub = n.advertise<std_msgs::Float64>("/simple_arm/joint_1_position_controller/command", 10);
ros::Publisher joint2_pub = n.advertise<std_msgs::Float64>("/simple_arm/joint_2_position_controller/command", 10);

Two publishers are declared, one for joint 1 commands, and one for joint 2 commands. The node handle will tell the ROS master that a Float64 message will be published on the joint topic. The node handle also sets the queue size to 10 in the second argument of the advertise function.

ros::Rate loop_rate(10);

A frequency of 10HZ is set using the loop_rate object. Rates are used in ROS to limit the frequency at which certain loops cycle. Choosing a rate that is too high may result in unnecessary CPU usage, while choosing a value too low could result in high latency. Choosing sensible values for all of the nodes in a ROS system is a bit of a fine art.

start_time = ros::Time::now().toSec();

We set start_time to the current time. In a moment we will use this to determine how much time has elapsed. When using ROS with simulated time (as we are doing here), ros-Time-now will initially return 0, until the first message has been received on the /clock topic. This is why start_time is set and polled continuously until a nonzero value is returned.

elapsed = ros::Time::now().toSec() - start_time;

In the main loop, the elapsed time is evaluated by measuring the current time and subtracting the start time.

std_msgs::Float64 joint1_angle, joint2_angle;
joint1_angle.data = sin(2 * M_PI * 0.1 * elapsed) * (M_PI / 2);
joint2_angle.data = sin(2 * M_PI * 0.1 * elapsed) * (M_PI / 2);

The joint angles are sampled from a sine wave with a period of 10 seconds, and in magnitude from [-pi/2, +pi/2].

joint1_pub.publish(joint1_angle);
joint2_pub.publish(joint2_angle);

Each trip through the body of the loop will result in two joint command messages being published.

loop_rate.sleep();

Due to the call to loop_rate.sleep() , the loop is traversed at approximately 10 Hertz. When the node receives the signal to shut down (either from the ROS Master, or via a signal from a console window), the loop will exit.